package com.sxf.core.db;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.sxf.common.exceptions.BusinessException;
import com.sxf.core.db.common.DatasourceStore;
import com.sxf.core.db.datasource.DynamicDataSource;
import com.sxf.core.db.datasource.DynamicDataSourceTransactionManager;
import com.sxf.core.db.datasource.DynamicPlugin;
import com.sxf.core.db.properties.DatasourceProperties;
import com.sxf.core.script.DbExecScriptClient;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;

import javax.sql.DataSource;
import java.net.MalformedURLException;
import java.sql.SQLException;
import java.util.Objects;

/**
 * @description: db启动类
 * @author: Leo
 * @date:
 */
@Configuration
public class DataSourceConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceConfig.class);

    @Autowired
    private DatasourceProperties datasourceProperties;

    /**
     * 设置连接池公共参数
     * 
     * @return
     */
    private HikariDataSource initHikariDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setMinimumIdle(datasourceProperties.getMinimumIdle());
        hikariDataSource.setMaximumPoolSize(datasourceProperties.getMaximumPoolSize());
        hikariDataSource.setAutoCommit(datasourceProperties.isAutoCommit());
        hikariDataSource.setIdleTimeout(datasourceProperties.getIdleTimeout());
        hikariDataSource.setPoolName(datasourceProperties.getPoolName());
        hikariDataSource.setMaxLifetime(datasourceProperties.getMaxLifetime());
        hikariDataSource.setConnectionTimeout(datasourceProperties.getConnectionTimeout());
        hikariDataSource.setConnectionTestQuery(datasourceProperties.getConnectionTestQuery());
        return hikariDataSource;
    }

    /**
     * 初始化读数据源
     *
     * @return
     */
    @Bean(name = "readDataSource")
    public DatasourceStore initReadDatasource() {
        DatasourceStore datasourceStore = new DatasourceStore();

        if (Objects.isNull(datasourceProperties.getRead()) || StringUtils
                .isAnyBlank(datasourceProperties.getRead().getDriverClass(), datasourceProperties.getRead().getUrl())) {
            return datasourceStore;
        }
        HikariDataSource hikariDataSource = initHikariDataSource();
        hikariDataSource.setDriverClassName(datasourceProperties.getRead().getDriverClass());
        hikariDataSource.setJdbcUrl(datasourceProperties.getRead().getUrl());
        hikariDataSource.setUsername(datasourceProperties.getRead().getUsername());
        hikariDataSource.setPassword(datasourceProperties.getRead().getPassword());

        datasourceStore.setCanUse(true);
        datasourceStore.setDataSource(hikariDataSource);
        return datasourceStore;
    }


    /**
     * 初始化写数据源
     * 
     * @return
     */
    @Bean(name = "writeDataSource")
    public DatasourceStore initWriteDataSource() {
        DatasourceStore datasourceStore = new DatasourceStore();

        if (Objects.isNull(datasourceProperties.getWrite()) || StringUtils
            .isAnyBlank(datasourceProperties.getWrite().getDriverClass(), datasourceProperties.getWrite().getUrl())) {
            throw new BusinessException("数据源初始化失败");
        }
        HikariDataSource hikariDataSource = initHikariDataSource();
        hikariDataSource.setDriverClassName(datasourceProperties.getWrite().getDriverClass());
        hikariDataSource.setJdbcUrl(datasourceProperties.getWrite().getUrl());
        hikariDataSource.setUsername(datasourceProperties.getWrite().getUsername());
        hikariDataSource.setPassword(datasourceProperties.getWrite().getPassword());

        datasourceStore.setCanUse(true);
        datasourceStore.setDataSource(hikariDataSource);
        return datasourceStore;
    }

    /**
     * 设置动态数据源 如果没有读数据源，将写数据源设置成读数据源
     *
     * @param readDataSource
     * @param writeDataSource
     * @return
     */
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(@Qualifier("readDataSource") DatasourceStore readDataSource,
                                        @Qualifier("writeDataSource") DatasourceStore writeDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        if (readDataSource.isCanUse()) {
            dynamicDataSource.setReadDataSource(readDataSource.getDataSource());
        } else {
            dynamicDataSource.setReadDataSource(writeDataSource.getDataSource());
        }
        dynamicDataSource.setWriteDataSource(writeDataSource.getDataSource());
        return dynamicDataSource;
    }

    /**
     * 初始化sessionFactory
     * 
     * @param dataSource
     * @param dynamicPlugin
     * @param paginationInterceptor
     * @return
     * @throws Exception
     */
    @Bean
    @Primary
    public SqlSessionFactory generateSqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource,
                                                       DynamicPlugin dynamicPlugin, PaginationInterceptor paginationInterceptor) throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        /**
         * 设置数据源
         */
        factoryBean.setDataSource(dataSource);

        /**
         * 设置mapper的xml文件。但是mapper的xml文件有可能没有。
         */
        try {
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(datasourceProperties.getMapperLocations()));
        } catch (Exception e) {
            LOGGER.warn("读取mapper location失败，但是暂不处理", e);
        }

        /**
         * 设置mybatis配置信息
         */
        factoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(datasourceProperties.getConfigLocation()));

        /**
         * 设置插件
         */
        factoryBean.setPlugins(dynamicPlugin, paginationInterceptor);
        return factoryBean.getObject();
    }

    /**
     * 初始化数据源分发插件
     *
     * @return
     */
    @Bean
    public DynamicPlugin dynamicPluginInterceptor() {
        return new DynamicPlugin();
    }

    /**
     * 初始化事务
     * 
     * @param dataSource
     * @return
     */
    @Bean
    public DynamicDataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
        return new DynamicDataSourceTransactionManager(dataSource);
    }

    /**
     * 初始化分页插件
     * 
     * @return
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        //分页条数不受限制
        paginationInterceptor.setLimit(-1L);
        return paginationInterceptor;
    }

    /**
     * 初始化 数据初始化组件
     * 
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceInitializer dataSourceInitializer(@Qualifier("dynamicDataSource") DataSource dataSource) {
        DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
        dataSourceInitializer.setDataSource(dataSource);
        dataSourceInitializer.setDatabasePopulator(databasePopulator());
        return dataSourceInitializer;
    }

    private DatabasePopulator databasePopulator() {
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        if (CollectionUtils.isNotEmpty(datasourceProperties.getSchema())) {
            datasourceProperties.getSchema().forEach(schema -> {
                try {
                    populator.addScript(new UrlResource(schema));
                } catch (MalformedURLException e) {
                    LOGGER.error("初始化schema失败", e);
                }
            });
        }
        if (CollectionUtils.isNotEmpty(datasourceProperties.getData())) {
            datasourceProperties.getData().forEach(data -> {
                try {
                    populator.addScript(new UrlResource(data));
                } catch (MalformedURLException e) {
                    LOGGER.error("初始化data失败", e);
                }
            });
        }
        return populator;
    }
    
    @Bean
    public DbExecScriptClient generateDbExecScriptClient(@Qualifier("dynamicDataSource") DataSource dataSource) throws SQLException {
        return new DbExecScriptClient(dataSource.getConnection());
    }

}
